package affcom;

import java.awt.*;
import java.util.*;

public class TCells implements LayoutManager2 {

// Author : Tom Yarker, 22695 Dorchester Drive, Geneseo, IL 61254
//          Phone: (309) 944-5955, E-Mail: tyarker@netexpress.net
// Purpose: Layout Manager for grid of variably sized columns and rows.
//          Width of each component set to largest of preferred widths
//          in its column. Height of each component set to preferred
//          height and centered vertically within its row. Height of
//          row is largest of preferred heights of its components.
//          Components are located by cell reference, either classic
//          spreadsheet form "A1", "A2", etcetera (which limits the
//          layout to 26 columns) or by a Point object encapsulating
//          the column and row numbers as x and y components. There
//          can be empty cells. Empty columns and rows are compressed
//          out.
// History: 07-26-96 Started

// Horizontal and vertical gaps
	private int     hgap, vgap;
// Components and their cell references
	private Vector  kids, refs;
// Flag to show size of grid is known
	private boolean       qnty;
// Column and row extents
	private int     iclo, ichi;
	private int     irlo, irhi;
	private int     ncol, nrow;
// Widths of columns and heights of rows
	private int[]   wdth, hite;
// Left edges of columns and tops of rows
	private int[]   lfte, tope;

//-----------------------------------------------------

// Constructor, gaps default to 8
	public TCells() {
		this(8, 8);
	}

//-----------------------------------------------------

// Constructor
// 1st argument is horizontal gap
// 2nd argument is   vertical gap
	public TCells(int hg, int vg) {
		hgap = hg;
		vgap = vg;
		kids = new Vector(16, 4);
		refs = new Vector(16, 4);
		qnty = false;
	}

//-----------------------------------------------------
// From this point on, routines are in alphabetic order
//-----------------------------------------------------

// Deprecated method to add a component to this layout
// 1st argument is a cell reference in the form
//      "An" where "A" is column and "n" is row
// 2nd argument is the component
	public void addLayoutComponent(String rf, Component co) {
		addLayoutComponent(co, rf);
	}

//-----------------------------------------------------

// Adds a component to this layout
// 1st argument is the component
// 2nd argument is a cell reference in any of 2 forms
//  a) a Point object, x is column and y is row
//     e.g. for column 4, row 6 - new Point(4, 6)
//  b) a String, "An" where "A" is column and "n" is row
//     e.g. for column 5, row 7 - "E7"
	public void addLayoutComponent(Component co, Object ob) {
// Handle null cell reference
		int     ic = 0;
		int     ir = 0;
		boolean ok = (ob == null);
// Handle Point cell reference
		if (ob instanceof Point) {
			ic = ((Point)ob).x;
			ir = ((Point)ob).y;
			ok = true;
		}
// Handle String cell reference
		if (ob instanceof String) {
			String st = ((String)ob).trim().toUpperCase();
			try {
				char ch = st.charAt(0);
				if (ch >= 'A' && ch <= 'Z') {
					ic = (int)(ch - 'A') + 1;
					st = st.substring(1);
					ir = Integer.parseInt(st);
					ok = true;
				}
			}
			catch (Exception ex) {}
		}
// Ignore component if there's no valid reference
		if (!ok) return;
// Prevent double insertion of component
		removeLayoutComponent(co);
// Prevent double use of the cell
		Point pt = new Point(ic, ir);
		int lo  =   refs.indexOf(pt);
		if (lo >= 0) {
			kids.removeElementAt(lo);
			refs.removeElementAt(lo);
		}
// Accept this component
		kids.addElement(co);
		refs.addElement(pt);
// Set flag to show row and column counts unknown
		qnty = false;
	}

//-----------------------------------------------------

// Returns the horizontal and vertical gaps
    public Dimension getGaps() {
		return new Dimension(hgap, vgap);
	}

//-----------------------------------------------------

// Returns the x alignment value
// 1st argument is the parent container
    public float getLayoutAlignmentX(Container pc) {
		return Component.CENTER_ALIGNMENT;
	}

//-----------------------------------------------------

// Returns the y alignment value
// 1st argument is the parent container
    public float getLayoutAlignmentY(Container pc) {
		return Component.CENTER_ALIGNMENT;
	}

//-----------------------------------------------------

// Returns numbers of columns and rows
	public Dimension getSize() {
// Only do the work if counts are unknown
		if (!qnty) {
			int nk = kids.size();
			if (nk == 0) {
				ncol = 0;
				nrow = 0;
			}
// Loop thru cell references to get extent
			else {
				iclo = Integer.MAX_VALUE;
				ichi = Integer.MIN_VALUE;
				irlo = iclo;
				irhi = ichi;
				for (int i = 0; i < nk; i = i + 1) {
					Point pt = (Point)refs.elementAt(i);
					iclo = Math.min(iclo, pt.x);
					ichi = Math.max(ichi, pt.x);
					irlo = Math.min(irlo, pt.y);
					irhi = Math.max(irhi, pt.y);
				}
// Determine the column and row counts
				ncol = ichi - iclo + 1;
				nrow = irhi - irlo + 1;
				qnty = true;
			}
		}
		return new Dimension(ncol, nrow);
	}

//-----------------------------------------------------

// Invalidates cached layout info
// 1st argument is the parent container
    public void invalidateLayout(Container pc) {}

//-----------------------------------------------------

// Lays out all the components
// 1st argument is the parent container
    public void layoutContainer(Container pc) {
// Check for an empty container
		int nk = kids.size();
		if (nk == 0) return;
// Get offsets from top-left
		Dimension cs = pc.getSize();
		Insets    in = pc.getInsets();
		Dimension ps = preferredLayoutSize(pc);
		int xo = (cs.width  - ps.width ) / 2 + in.left + hgap;
		int yo = (cs.height - ps.height) / 2 + in.top  + vgap;
// Loop thru components, locating and sizing
		for (int i = 0; i < nk; i = i + 1) {
			Component co = (Component)kids.elementAt(i);
			if (!co.isVisible()) continue;
			Dimension sz = co.getPreferredSize();
			int ht = sz.height;
			Point pt = (Point)refs.elementAt(i);
			int ic = pt.x - iclo;
			int ir = pt.y - irlo;
			int wd =  wdth[ic];
			ic = xo + lfte[ic];
			ir = yo + tope[ir] + (hite[ir] - ht) / 2;
			co.setBounds(ic, ir, wd, ht);
		}
	}

//-----------------------------------------------------

// Returns the maximum size of the layout
// 1st argument is the parent container
    public Dimension maximumLayoutSize(Container pc) {
		int mx = Short.MAX_VALUE;
		return new Dimension(mx, mx);
	}

//-----------------------------------------------------

// Returns the minimum size of the layout
// 1st argument is the parent container
    public Dimension minimumLayoutSize(Container pc) {
		return preferredLayoutSize(pc);
	}

//-----------------------------------------------------

// Returns the preferred size of the layout
// 1st argument is the parent container
    public Dimension preferredLayoutSize(Container pc) {
		getSize();
		int pw = 0;
		int ph = 0;
// Get widest  component in each column
// and highest component in each row
		if (qnty) {
			wdth = new int[ncol];
			hite = new int[nrow];
			int nk = kids.size();
			for (int i = 0; i < nk; i = i + 1) {
				Component co = (Component)kids.elementAt(i);
				if (!co.isVisible()) continue;
				Dimension sz = co.getPreferredSize();
				Point pt = (Point)refs.elementAt(i);
				int ic = pt.x - iclo;
				int ir = pt.y - irlo;
				wdth[ic] = Math.max(wdth[ic], sz.width );
				hite[ir] = Math.max(hite[ir], sz.height);
			}
// Get offsets to left edge of columns
//         and to top  edge of rows
			lfte = new int[ncol];
			for (int i = 0; i < ncol; i = i + 1) {
				int cw = wdth[i];
				if (pw > 0 && cw > 0) pw = pw + hgap;
				lfte[i] = pw;
				pw = pw + cw;
			}
			tope = new int[nrow];
			for (int i = 0; i < nrow; i = i + 1) {
				int rh = hite[i];
				if (ph > 0 && rh > 0) ph = ph + vgap;
				tope[i] = ph;
				ph = ph + rh;
			}
// Add gaps and insets to preferred size
			if (pw > 0) pw = hgap + pw + hgap;
			if (ph > 0) ph = vgap + ph + vgap;
		}
		Insets in = pc.getInsets();
		pw = in.left + pw + in.right ;
		ph = in.top  + ph + in.bottom;
		return new Dimension(pw, ph);
	}

//-----------------------------------------------------

// Removes a component from this layout
// 1st argument is the component
	public void removeLayoutComponent(Component co) {
		int lo  =   kids.indexOf(co);
		if (lo >= 0) {
			kids.removeElementAt(lo);
			refs.removeElementAt(lo);
// Set flag to show row and column counts unknown
			qnty = false;
		}
	}

//-----------------------------------------------------

// Sets the horizontal and vertical gaps
// 1st argument is horizontal gap
// 2nd argument is   vertical gap
	public void setGaps(int hg, int vg) {
		hgap = hg;
		vgap = vg;
	}

//-----------------------------------------------------

// Provides desciptive string
	public String toString() {
		String st = "TYCells[components=" + kids.size();
		if (qnty)
			st = st + ",columns=" + ncol + ",rows=" + nrow;
		return st + "]";
	}

//-----------------------------------------------------

}